home *** CD-ROM | disk | FTP | other *** search
/ Programming an RTS Game with Direct3D / Programming an RTS Game with Direct3D.iso / Examples / Chapter 14 / Example 14.1 / unit.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2006-07-17  |  17.0 KB  |  732 lines

  1. #include "unit.h"
  2. #include "groupai.h"
  3. #include "player.h"
  4.  
  5. std::vector<SKINNEDMESH*> unitMeshes;
  6.  
  7. void LoadUnitResources(IDirect3DDevice9* m_pDevice)
  8. {
  9.     std::vector<std::string> fnames;
  10.  
  11.     fnames.push_back("units/drone.x");
  12.     fnames.push_back("units/soldier.x");
  13.     fnames.push_back("units/magician.x");
  14.  
  15.     for(int i=0;i<fnames.size();i++)
  16.     {
  17.         SKINNEDMESH *newMesh = new SKINNEDMESH();
  18.         newMesh->Load((char*)fnames[i].c_str(), m_pDevice);
  19.         unitMeshes.push_back(newMesh);
  20.     }
  21. }
  22.  
  23. void UnloadUnitResources()
  24. {
  25.     for(int i=0;i<unitMeshes.size();i++)
  26.         if(unitMeshes[i] != NULL)
  27.             delete unitMeshes[i];
  28.  
  29.     unitMeshes.clear();
  30. }
  31.  
  32. //////////////////////////////////////////////////////////////////////////////////
  33. //                                UNIT                                            //
  34. //////////////////////////////////////////////////////////////////////////////////
  35.  
  36. UNIT::UNIT(int _type, int _team, INTPOINT mp, TERRAIN *_terrain, PLAYER *_player, IDirect3DDevice9* Dev) : MAPOBJECT()
  37. {
  38.     m_type = _type;
  39.     m_team = _team;
  40.     m_mappos = mp;
  41.     m_pTerrain = _terrain;
  42.     m_pDevice = Dev;
  43.     m_mapsize.Set(0, 0);
  44.     m_time = m_pauseTime = 0.0f;
  45.     m_animation = m_activeWP = 0;
  46.     m_rotation = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
  47.     m_scale = D3DXVECTOR3(0.2f, 0.2f, 0.2f);
  48.     m_isBuilding = m_moving = false;
  49.     m_movePrc = 0.0f;
  50.     m_state = STATE_IDLE;
  51.     m_attackTime = 0.0f;
  52.     m_player = _player;
  53.     m_mana = 0.0f;
  54.  
  55.     if(m_pTerrain != NULL)
  56.     {
  57.         m_position = m_pTerrain->GetWorldPos(m_mappos);
  58.         MAPTILE *tile = m_pTerrain->GetTile(m_mappos);
  59.         if(tile != NULL)tile->m_pMapObject = this;
  60.     }
  61.     else m_position = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
  62.  
  63.     if(m_type == WORKER)
  64.     {
  65.         m_hp = m_hpMax = 100;
  66.         m_range = 1;
  67.         m_damage = 5;
  68.         m_sightRadius = 7;
  69.         m_speed = 1.0f;
  70.         m_name = "Farmer";
  71.     }    
  72.     else if(m_type == SOLDIER)
  73.     {
  74.         m_hp = m_hpMax = 180;
  75.         m_range = 1;
  76.         m_damage = 12;
  77.         m_sightRadius = 8;
  78.         m_speed = 0.8f;
  79.         m_name = "Soldier";
  80.     }    
  81.     else if(m_type == MAGICIAN)
  82.     {
  83.         m_hp = m_hpMax = 100;
  84.         m_range = 4;
  85.         m_damage = 8;
  86.         m_sightRadius = 10;
  87.         m_speed = 1.1f;
  88.         m_name = "Magician";
  89.     }
  90.  
  91.     m_pAnimControl = unitMeshes[m_type]->GetAnimationControl();
  92.     if(m_pAnimControl != NULL)
  93.         m_pAnimControl->ResetTime();
  94.  
  95.     SetAnimation("Still");
  96. }
  97.  
  98. UNIT::~UNIT()
  99. {
  100.     if(m_pTerrain)m_pTerrain->m_updateSight = true;
  101. }
  102.  
  103. void UNIT::Render()
  104. {
  105.     try
  106.     {
  107.         if(m_visible && m_type < unitMeshes.size() && unitMeshes[m_type] != NULL)
  108.         {
  109.             SetAnimation(m_animation);
  110.  
  111.             unitMeshes[m_type]->SetPose(GetWorldMatrix(), m_pAnimControl, m_time);
  112.             unitMeshes[m_type]->Render(NULL);
  113.             m_time = 0.0f;
  114.  
  115.             //Extract staff position
  116.             if(m_type == MAGICIAN)
  117.             {
  118.                 BONE *staff = unitMeshes[m_type]->FindBone("Staff");
  119.  
  120.                 if(staff != NULL)
  121.                 {
  122.                     D3DXMATRIX mat = staff->CombinedTransformationMatrix;
  123.                     m_staffPos = D3DXVECTOR3(mat(3, 0), mat(3, 1), mat(3, 2));
  124.                 }
  125.             }    
  126.         }
  127.     }
  128.     catch(...)
  129.     {
  130.         debug.Print("Error in UNIT::Render()");
  131.     }    
  132. }
  133.  
  134. void UNIT::Update(float deltaTime)
  135. {
  136.     try
  137.     {
  138.         if(m_type == MAGICIAN && m_mana < 50)
  139.             m_mana += deltaTime;
  140.  
  141.         //Pause the units...
  142.         if(m_pauseTime > 0.0f && !m_dead)
  143.         {
  144.             m_pauseTime -= deltaTime;
  145.             return;
  146.         }
  147.  
  148.         m_staffPos = m_position + D3DXVECTOR3(0.0f, 1.0f, 0.0f);
  149.  
  150.         //update unit animation time
  151.         if(m_dead)
  152.         {
  153.             m_selected = false;
  154.             m_state = STATE_DEAD;
  155.             m_time += deltaTime * 0.3f;
  156.  
  157.             //Shrink sightradius when a unit has died
  158.             int oldSr = m_sightRadius;
  159.             if(m_sightRadius > 0.0f)m_sightRadius -= deltaTime;
  160.             if(m_sightRadius < 0.0f)m_sightRadius = 0.0f;
  161.             if(oldSr != (int)m_sightRadius)m_pTerrain->m_updateSight = true;
  162.                 
  163.             float duration = unitMeshes[m_type]->GetAnimationDuration(ANIM_DIE) - 0.05f;
  164.             if(m_pAnimControl->GetTime() > duration)
  165.             {                
  166.                 m_position.y -= deltaTime * 0.02f;
  167.                 m_time = 0.0f;
  168.                 m_pAnimControl->SetTrackPosition(0, duration);
  169.             }
  170.         }
  171.         else if(m_state == STATE_ATTACK)
  172.         {
  173.             m_time += deltaTime * 0.5f;
  174.             float attackDuration = unitMeshes[m_type]->GetAnimationDuration(ANIM_ATTACK);
  175.             if(m_pAnimControl->GetTime() > attackDuration)
  176.             {
  177.                 m_pAnimControl->SetTrackPosition(0, 0.0);
  178.                 m_time = 0.0f;
  179.             }
  180.  
  181.             m_attackTime += deltaTime;
  182.         }
  183.         else m_time += deltaTime * 0.8f * m_speed;
  184.  
  185.         //Check that the unit is standing on a walkable tile
  186.         MAPTILE *tile = m_pTerrain->GetTile(m_mappos);
  187.         if(tile == NULL || !tile->m_walkable)
  188.         {
  189.             m_mappos = m_pTerrain->GetClosestFreeTile(m_mappos, m_mappos);
  190.             MoveUnit(m_mappos);
  191.             m_position = m_pTerrain->GetWorldPos(m_mappos);
  192.             m_moving = false;
  193.         }
  194.  
  195.         //if the unit is moving...
  196.         if(m_moving && !m_dead)
  197.         {
  198.             if(m_movePrc < 1.0f)m_movePrc += deltaTime * m_speed;
  199.             if(m_movePrc > 1.0f)m_movePrc = 1.0f;
  200.             
  201.             //Goal reached
  202.             if(m_movePrc == 1.0f)
  203.             {
  204.                 if(m_activeWP + 1 >= m_path.size())
  205.                 {
  206.                     m_moving = false;
  207.                     SetAnimation("Still");                
  208.                     if(m_mappos != m_finalGoal)
  209.                         Goto(m_finalGoal, false, true, m_state);
  210.                 }
  211.                 else if(!CheckCollision(m_path[m_activeWP + 1]) && !UnitAI(true)) //Next Waypoint
  212.                 {            
  213.                     m_activeWP++;
  214.                     SetAnimation("Run");
  215.                     MoveUnit(m_path[m_activeWP]);
  216.                 }            
  217.             }
  218.  
  219.             //Interpolate position between m_lastWP and m_nextWP
  220.             m_position = m_lastWP * (1.0f - m_movePrc) + m_nextWP * m_movePrc;
  221.         }
  222.     }
  223.     catch(...)
  224.     {
  225.         debug.Print("Error in UNIT::Update()");
  226.     }
  227. }
  228.  
  229. bool UNIT::CheckCollision(INTPOINT mp)
  230. {
  231.     MAPTILE *tile = m_pTerrain->GetTile(mp);
  232.     if(tile == NULL || m_dead)return false;
  233.     
  234.     try
  235.     {
  236.         if(tile->m_pMapObject != NULL && tile->m_pMapObject != this)    //Collision with another unit
  237.         {
  238.             UNIT *otherUnit = (UNIT*)tile->m_pMapObject;
  239.  
  240.             //The other unit is m_moving
  241.             if(otherUnit->m_moving && otherUnit->m_pauseTime <= 0.0f && m_speed <= otherUnit->m_speed)
  242.             {
  243.                 //Pause the unit and wait for the other one to move 
  244.                 Pause((100 + rand()%200) / 1000.0f);
  245.                 m_path.clear();
  246.             }
  247.             else    //Recalculate path
  248.             {
  249.                 //Find next unoccupied walkable tile
  250.                 INTPOINT tempGoal = m_mappos;
  251.                 for(int i=m_activeWP+1;i<m_path.size();i++)
  252.                 {
  253.                     MAPTILE *tile = m_pTerrain->GetTile(m_path[i]);
  254.                     if(tile != NULL)
  255.                         if(tile->m_walkable && tile->m_pMapObject == NULL)
  256.                         {
  257.                             tempGoal = m_path[i];
  258.                             break;
  259.                         }
  260.                 }
  261.  
  262.                 //No available tile found 
  263.                 if(tempGoal == m_mappos)
  264.                 {
  265.                     //Move to tile closest to the original goal
  266.                     INTPOINT newGoal = m_pTerrain->GetClosestFreeTile(m_finalGoal, m_mappos);
  267.  
  268.                     if(newGoal == m_mappos || m_mappos.Distance(m_finalGoal) < 2.0f || !m_pTerrain->Within(newGoal))
  269.                     {
  270.                         m_moving = false;
  271.                         SetAnimation("Still");
  272.                         m_state = STATE_IDLE;
  273.                     }
  274.                     else Goto(newGoal, false, true, m_state);
  275.                 }
  276.                 else 
  277.                 {
  278.                     //Move to tempGoal to avoid unit, then continue to finalGoal
  279.                     Goto(tempGoal, true, false, m_state); 
  280.                 }
  281.             }
  282.  
  283.             return true;    //A Collision happened
  284.         }
  285.     }
  286.     catch(...)
  287.     {
  288.         debug.Print("Error in UNIT::CheckCollision()");
  289.     }
  290.  
  291.     return false;        //No Collision
  292. }
  293.  
  294. void UNIT::Goto(INTPOINT mp, bool considerUnits, bool _finalGoal, int newState)
  295. {
  296.     if(m_pTerrain == NULL || m_dead || !m_pTerrain->Within(mp))return;
  297.     if(_finalGoal)m_finalGoal = mp;
  298.     
  299.     try
  300.     {
  301.         //Clear old path
  302.         m_path.clear();
  303.         m_activeWP = 0;
  304.         m_state = newState;
  305.  
  306.         if(m_moving)        //If unit is currently moving
  307.         {
  308.             //Finish the active waypoint 
  309.             m_path.push_back(m_mappos);
  310.             std::vector<INTPOINT> tmpPath = m_pTerrain->GetPath(m_mappos, mp, considerUnits, this);
  311.  
  312.             //add new path
  313.             for(int i=0;i<tmpPath.size();i++)
  314.                 m_path.push_back(tmpPath[i]);
  315.         }
  316.         else        //Create new path from scratch...
  317.         {
  318.             m_path = m_pTerrain->GetPath(m_mappos, mp, considerUnits, this);
  319.  
  320.             if(m_path.size() > 0)        //if a path was found
  321.             {
  322.                 m_moving = true;
  323.  
  324.                 //Check that the next tile is free
  325.                 if(!CheckCollision(m_path[m_activeWP]))
  326.                 {
  327.                     MoveUnit(m_path[m_activeWP]);
  328.                     SetAnimation("Run");                
  329.                 }
  330.             }
  331.             else Pause(0.1f);
  332.         }
  333.     }
  334.     catch(...)
  335.     {
  336.         debug.Print("Error in UNIT::Goto()");
  337.     }
  338. }
  339.  
  340. void UNIT::MoveUnit(INTPOINT to)
  341. {
  342.     if(!m_pTerrain->Within(to))return;
  343.  
  344.     try
  345.     {
  346.         if(m_mappos.Distance(to) >= 2.0f)
  347.         {
  348.             m_state = STATE_IDLE;
  349.             MoveUnit(m_mappos);
  350.             m_moving = false;
  351.             return;
  352.         }
  353.  
  354.         m_lastWP = m_pTerrain->GetWorldPos(m_mappos);
  355.         m_rotation = GetDirection(m_mappos, to);
  356.  
  357.         //Clear old MAPTILE unit pointer
  358.         MAPTILE *tile = m_pTerrain->GetTile(m_mappos);
  359.         if(tile != NULL)tile->m_pMapObject = NULL;
  360.  
  361.         m_mappos = to;    //New m_mappos
  362.         m_movePrc = 0.0f;
  363.         m_nextWP = m_pTerrain->GetWorldPos(m_mappos);
  364.  
  365.         //Set new MAPTILE unit pointer
  366.         tile = m_pTerrain->GetTile(m_mappos);
  367.         if(tile != NULL)tile->m_pMapObject = this;
  368.  
  369.         m_pTerrain->m_updateSight = true;
  370.     }
  371.     catch(...)
  372.     {
  373.         debug.Print("Error in UNIT::MoveUnit()");
  374.     }
  375. }
  376.  
  377. void UNIT::Pause(float time)
  378. {
  379.     SetAnimation("Still");
  380.     m_pauseTime = time;
  381. }
  382.  
  383. BBOX UNIT::GetBoundingBox()
  384. {
  385.     if(m_type == 0)        //Farmer
  386.         return BBOX(m_position + D3DXVECTOR3(0.3f, 1.0f, 0.3f), m_position - D3DXVECTOR3(0.3f, 0.0f, 0.3f));
  387.     else if(m_type == 1)    //Soldier
  388.         return BBOX(m_position + D3DXVECTOR3(0.35f, 1.2f, 0.35f), m_position - D3DXVECTOR3(0.35f, 0.0f, 0.35f));
  389.     else if(m_type == 2)    //Magician
  390.         return BBOX(m_position + D3DXVECTOR3(0.3f, 1.1f, 0.3f), m_position - D3DXVECTOR3(0.3f, 0.0f, 0.3f));
  391. }
  392.  
  393. D3DXMATRIX UNIT::GetWorldMatrix()
  394. {
  395.     D3DXMATRIX s, p, r;
  396.     D3DXMatrixTranslation(&p, m_position.x, m_position.y, m_position.z);
  397.     D3DXMatrixRotationYawPitchRoll(&r, m_rotation.y, m_rotation.x, m_rotation.z);
  398.     D3DXMatrixScaling(&s, m_scale.y, m_scale.x, m_scale.z);
  399.     return s * r * p;
  400. }
  401.  
  402. D3DXVECTOR3 UNIT::GetDirection(INTPOINT p1, INTPOINT p2)
  403. {
  404.     int dx = p2.x - p1.x, dy = p2.y - p1.y;
  405.     
  406.     if(dx < 0 && dy > 0)    return D3DXVECTOR3(0.0f, D3DX_PI/4,        0.0f); 
  407.     if(dx == 0 && dy > 0)    return D3DXVECTOR3(0.0f, 0.0f,            0.0f);
  408.     if(dx > 0 && dy > 0)    return D3DXVECTOR3(0.0f, -D3DX_PI/4,    0.0f); 
  409.     if(dx > 0 && dy == 0)    return D3DXVECTOR3(0.0f, -D3DX_PI/2,    0.0f);
  410.     if(dx > 0 && dy < 0)    return D3DXVECTOR3(0.0f, (-D3DX_PI/4)*3,0.0f); 
  411.     if(dx == 0 && dy < 0)    return D3DXVECTOR3(0.0f, D3DX_PI,        0.0f);
  412.     if(dx < 0 && dy < 0)    return D3DXVECTOR3(0.0f, (D3DX_PI/4)*3,    0.0f); 
  413.     if(dx < 0 && dy == 0)    return D3DXVECTOR3(0.0f, D3DX_PI/2,        0.0f);
  414.  
  415.     return m_rotation;
  416. }
  417.  
  418. void UNIT::SetAnimation(char name[])
  419. {
  420.     ID3DXAnimationSet *anim = NULL;
  421.     m_animation = 0;
  422.  
  423.     for(int i=0;i<m_pAnimControl->GetMaxNumAnimationSets();i++)
  424.     {
  425.         anim = NULL;
  426.         m_pAnimControl->GetAnimationSet(i, &anim);
  427.  
  428.         if(anim != NULL)
  429.         {
  430.             if(strcmp(name, anim->GetName()) == 0)
  431.             {
  432.                 m_pAnimControl->SetTrackAnimationSet(0, anim);
  433.                 m_pAnimControl->SetTrackPosition(0, 0.0f);
  434.                 m_pAnimControl->ResetTime();
  435.                 m_animation = i;
  436.             }
  437.             anim->Release();
  438.         }
  439.     }
  440. }
  441.  
  442. void UNIT::SetAnimation(int index)
  443. {
  444.     ID3DXAnimationSet *anim = NULL;
  445.     m_pAnimControl->GetAnimationSet(index, &anim);
  446.     if(anim != NULL)m_pAnimControl->SetTrackAnimationSet(0, anim);
  447.     anim->Release();
  448. }
  449.  
  450. bool UNIT::UnitAI(bool newMapTile)
  451. {
  452.     if(m_dead)return false;
  453.  
  454.     bool result = false;
  455.  
  456.     try
  457.     {
  458.         std::vector<MAPOBJECT*> enemies = GetTargetsWithinRange(m_sightRadius);
  459.         
  460.         //Report enemy sightings
  461.         if(m_pGroup != NULL && !enemies.empty())
  462.             m_pGroup->EnemiesSpotted(enemies);
  463.  
  464.         switch(m_state)
  465.         {    
  466.             case STATE_IDLE:
  467.             {
  468.                 m_animation = ANIM_STILL;
  469.  
  470.                 if(enemies.size() > 0)
  471.                 {
  472.                     m_pTarget = BestTargetToAttack(enemies);
  473.                     m_state = STATE_ATTACK;
  474.                     result = true;
  475.                 }
  476.                 
  477.                 Heal();
  478.  
  479.                 break;
  480.             }
  481.             case STATE_MOVING:
  482.             {
  483.                 m_animation = ANIM_RUN;
  484.  
  485.                 if(!m_moving)
  486.                 {
  487.                     m_state = STATE_IDLE;
  488.                     m_animation = ANIM_STILL;
  489.                 }
  490.  
  491.                 break;
  492.             }
  493.             case STATE_DEAD:
  494.             {
  495.                 //Stay m_dead
  496.                 m_dead = true;
  497.                 break;
  498.             }
  499.             case STATE_SEARCH:
  500.             {
  501.                 if(enemies.size() > 1)
  502.                     m_pTarget = BestTargetToAttack(enemies);
  503.  
  504.                 if(m_pTarget == NULL || m_pTarget->m_dead)
  505.                 {
  506.                     m_state = STATE_IDLE;
  507.                     break;
  508.                 }
  509.  
  510.                 //Attack if m_pTarget is within m_range
  511.                 if(newMapTile || !m_moving)
  512.                 {
  513.                     if(m_mappos.inRect(m_pTarget->GetMapRect(m_range)))
  514.                     {
  515.                         m_state = STATE_ATTACK;
  516.                         SetAnimation("Attack");
  517.                         m_moving = false;
  518.                         m_attackTime = 0.0f;
  519.                         m_time = unitMeshes[m_type]->GetAnimationDuration(ANIM_ATTACK);
  520.                         m_rotation = GetDirection(m_mappos, m_pTarget->m_mappos);
  521.                         result = true;
  522.                     }
  523.                     else
  524.                     {
  525.                         INTPOINT attackPos = m_pTarget->GetAttackPos(m_mappos); 
  526.                         if(!m_pTerrain->Within(attackPos))
  527.                         {
  528.                             m_state = STATE_IDLE;
  529.                             break;
  530.                         }
  531.  
  532.                         //Update path
  533.                         if(!m_moving || attackPos != m_finalGoal)
  534.                         {                    
  535.                             bool considerUnit = m_mappos.Distance(m_pTarget->m_mappos) <= 5;
  536.                             Goto(attackPos, considerUnit, true, m_state);
  537.                         }
  538.                     }
  539.                 }
  540.  
  541.                 break;
  542.             }
  543.             case STATE_ATTACK:
  544.             {
  545.                 if(m_pTarget == NULL || m_pTarget->m_dead){m_state = STATE_IDLE; return false;}
  546.  
  547.                 //Heal other units
  548.                 Heal();
  549.  
  550.                 if(m_mappos.inRect(m_pTarget->GetMapRect(m_range)))
  551.                 {
  552.                     //Attack
  553.                     if(m_attackTime > 3.0f)
  554.                     {
  555.                         m_attackTime = 0.0f;
  556.                         m_animation = ANIM_ATTACK;
  557.                         m_rotation = GetDirection(m_mappos, m_pTarget->m_mappos);
  558.                         m_pAnimControl->ResetTime();
  559.  
  560.                         //Magician Fireball effect
  561.                         if(m_type == MAGICIAN)
  562.                             effects.push_back(new EFFECT_FIREBALL(m_pDevice, &m_staffPos, m_pTarget, this));
  563.                         else m_pTarget->Damage(m_damage, this);
  564.                     }
  565.                 }
  566.                 else m_state = STATE_SEARCH;
  567.  
  568.                 break;
  569.             }
  570.             case STATE_RETREAT:
  571.             {    
  572.                 if(!m_moving)
  573.                     m_state = STATE_IDLE;
  574.  
  575.                 break;
  576.             }
  577.             case STATE_GOTO_BUILD:
  578.             {        
  579.                 if(!m_moving)Goto(m_buildingPosition, false, true, STATE_GOTO_BUILD);
  580.  
  581.                 BUILDING *temp = new BUILDING(m_buildingToPlace, m_team, true, m_buildingPosition, m_pTerrain, m_player, false, m_pDevice);
  582.  
  583.                 if(m_mappos.inRect(temp->GetMapRect(0)))
  584.                 {
  585.                     if(m_player->money < GetCost(m_buildingToPlace, true))
  586.                     {
  587.                         m_state = STATE_IDLE;                
  588.                     }
  589.                     else
  590.                     {
  591.                         m_selected = false;
  592.                         m_player->RemoveMapObject(this);
  593.                         BUILDING *build = (BUILDING*)m_player->AddMapObject(m_buildingToPlace, m_buildingPosition, true, false);
  594.                         build->m_pTarget = this;
  595.                         m_player->money -= GetCost(m_buildingToPlace, true);
  596.                         m_state = STATE_BUILD;
  597.                     }
  598.                 }
  599.  
  600.                 delete temp;
  601.  
  602.                 break;
  603.             }
  604.             case STATE_BUILD:
  605.             {    
  606.                 m_state = STATE_IDLE;
  607.                 break;
  608.             }
  609.         }
  610.     }
  611.     catch(...)
  612.     {
  613.         debug.Print("Error in UNIT::UnitAI()");
  614.     }
  615.  
  616.     return result;
  617. }
  618.  
  619. void UNIT::Attack(MAPOBJECT *_target)
  620. {
  621.     if(m_dead || _target == NULL)return;
  622.  
  623.     m_pTarget = _target;
  624.     m_state = STATE_SEARCH;
  625.     UnitAI(false);
  626. }
  627.  
  628. void UNIT::ConstructBuilding(int buildToPlace, INTPOINT pos)
  629. {
  630.     if(m_type != WORKER)return;
  631.  
  632.     Goto(pos, false, true, STATE_GOTO_BUILD);
  633.     m_buildingToPlace = buildToPlace;
  634.     m_buildingPosition = pos;
  635.     UnitAI(false);
  636. }
  637.  
  638. bool UNIT::isDead()
  639. {
  640.     if(!m_dead || m_pTerrain == NULL)return false;
  641.  
  642.     D3DXVECTOR3 worldPos = m_pTerrain->GetWorldPos(m_mappos);
  643.     return m_dead && (worldPos.y - m_position.y) > 3.0f;
  644. }
  645.  
  646. void UNIT::Damage(int dmg, MAPOBJECT* attacker)
  647. {
  648.     if(m_dead)return;
  649.  
  650.     try
  651.     {
  652.         m_hp -= dmg;
  653.  
  654.         if(m_hp <= 0)
  655.         {
  656.             m_hp = 0;
  657.             m_dead = true;
  658.  
  659.             UNIT *attackUnit = (UNIT*)attacker;
  660.             if(attackUnit != NULL && attackUnit->m_player != NULL)
  661.                 attackUnit->m_player->m_numKills++;
  662.  
  663.             //Play m_dead m_animation
  664.             SetAnimation("Die");
  665.             m_time = 0.0f;
  666.             m_pTerrain->m_updateSight = true;
  667.             
  668.             //Reset mapTile->mapObject to NULL
  669.             if(m_pTerrain != NULL)
  670.             {
  671.                 MAPTILE *tile = m_pTerrain->GetTile(m_mappos);
  672.                 if(tile != NULL)tile->m_pMapObject = NULL;
  673.             }
  674.  
  675.             //Leave group
  676.             if(m_pGroup != NULL)
  677.             {
  678.                 m_pGroup->RemoveMember(this);
  679.                 m_pGroup = NULL;
  680.             }
  681.         }
  682.     }
  683.     catch(...)
  684.     {
  685.         debug.Print("Error in UNIT::Damage()");
  686.     }
  687. }
  688.  
  689. void UNIT::Heal()
  690. {
  691.     //Heal Spell
  692.     if(m_type == MAGICIAN && m_mana > 30.0f)
  693.     {
  694.         RECT r = GetMapRect(m_sightRadius - 1);
  695.         UNIT *bestUnit = NULL;
  696.         float healthPrc = 0.6f;
  697.  
  698.         //Find the friendly unit with the lowest health
  699.         for(int y=r.top;y<=r.bottom;y++)
  700.             for(int x=r.left;x<=r.right;x++)
  701.             {
  702.                 MAPTILE* tile = m_pTerrain->GetTile(x, y);
  703.  
  704.                 if(tile != NULL && tile->m_pMapObject != NULL && !tile->m_pMapObject->m_dead &&
  705.                     tile->m_pMapObject->m_team == m_team && !tile->m_pMapObject->m_isBuilding &&
  706.                     tile->m_pMapObject->m_hp < tile->m_pMapObject->m_hpMax)
  707.                 {    
  708.                     float h = tile->m_pMapObject->m_hp / (float)tile->m_pMapObject->m_hpMax;
  709.  
  710.                     if(h < healthPrc)
  711.                     {
  712.                         healthPrc = h;
  713.                         bestUnit = (UNIT*)tile->m_pMapObject;
  714.                     }
  715.                 }
  716.             }
  717.  
  718.         //Cast Heal Spell
  719.         if(bestUnit != NULL)
  720.         {
  721.             bestUnit->m_hp += bestUnit->m_hpMax * 0.5f;
  722.             if(bestUnit->m_hp > bestUnit->m_hpMax)
  723.                 bestUnit->m_hp = bestUnit->m_hpMax;
  724.  
  725.             effects.push_back(new EFFECT_SPELL(m_pDevice, bestUnit->m_position + D3DXVECTOR3(0.0f, 0.1f, 0.0f)));
  726.             Pause(3.0f);
  727.             bestUnit->Pause(5.0f);
  728.             bestUnit->m_animation = ANIM_STILL;            
  729.             m_mana -= 30.0f;
  730.         }
  731.     }
  732. }